
{
open Parser
exception Eof

let keyword_table = Hashtbl.create 53
let _ =
  List.iter (fun (kwd, tok) -> Hashtbl.add keyword_table kwd tok)
    [ "false", KW_FALSE;
      "true", KW_TRUE;
      "let", KW_LET;
      "rec", KW_REC;
      "and", KW_AND;
      "in", KW_IN;
      "match", KW_MATCH;
      "with", KW_WITH;
      "if", KW_IF;
      "then", KW_THEN;
      "else", KW_ELSE;
      "fun", KW_FUN;
      "not", KW_NOT;
      "begin", KW_BEGIN;
      "end", KW_END;
      "use", KW_USE;
      "function", KW_FUNCTION;
      "to_string", KW_TO_STRING;
      "write", KW_WRITE;
      "or", KW_OR;
      "ref", KW_REF
    ]
}

let space = ' '|'\n'|'\r'|'\t'
let dec_digit = ['0'-'9']
let int_num = dec_digit+
let id_char = ['a'-'z''A'-'Z''_''0'-'9']
let decorator = ['\'''?''!']
let id = ['a'-'z']id_char*decorator*
let comment = "(*" ([^'*'] | ('*'[^')']))* "*)"

rule token = parse
  space { token lexbuf }
| comment { token lexbuf }
| id { let id = Lexing.lexeme lexbuf in
  try
    Hashtbl.find keyword_table id
  with Not_found -> ID id }
| int_num { INT(int_of_string (Lexing.lexeme lexbuf)) }
| '"' { STRING(str lexbuf) }
| '(' { OPEN }
| ')' { CLOSE }
| '[' { LBRACK }
| ']' { RBRACK }
| '|' { OR }
| "->" { ARROW }
| ";;" { EOL }
| ';' { SEMICOLON }
| ':' { COLON }
| ',' { COMMA }
| '_' { ANY }
| '+' { PLUS }
| '-' { MINUS }
| "~-" { UMINUS }
| '*' { TIMES }
| '/' { DIVIDE }
| '=' { EQ }
| "<>" { NE }
| '<' { LT }
| '>' { GT }
| "<=" { LE }
| ">=" { GE }
| '&' { AND }
| '^' { CONCAT }
| "&&" { LAND }
| "||" { LOR }
| '#' { SHARP }
| '$' { DOLLAR }
| "::" { CONS }
| ":=" { ASSIGN }
| '!' { DEREF }
| eof { raise Eof }

and str = parse
  [^'"''\\']*'"'
    { let s = Lexing.lexeme lexbuf in
      String.sub s 0 ((String.length s) - 1)
    }
| [^'"''\\']*'\\'
    { let s = Lexing.lexeme lexbuf in
      let l = String.length s in
      let sp = special lexbuf in
      let following = str lexbuf in
      (String.sub s 0 (l - 1)) ^ sp ^ following
    }
| eof { raise Eof }

and special = parse
  'r' { "\r" }
| 'n' { "\n" }
| 'b' { "\b" }
| 't' { "\t" }
| '\'' { "'" }
| '"' { "\"" }
| _ { Lexing.lexeme lexbuf }
| eof { raise Eof }
      
